package com.neuralnoise.twitter;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.LinkedList;
import java.util.TreeSet;

import twitter4j.IDs;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.OAuthAuthorization;
import twitter4j.conf.ConfigurationBuilder;

import com.neuralnoise.AbstractDriver;
import com.neuralnoise.TwitterDB;
import com.neuralnoise.foaf.core.Person;
import com.neuralnoise.geo.GeoUtils;
import com.neuralnoise.geo.GeoUtils.ServiceType;
import com.neuralnoise.hibernate.geo.GeoLocation;
import com.neuralnoise.hibernate.twitter.Profile;
import com.neuralnoise.hibernate.twitter.Screen;

public class TwitterDriver extends AbstractDriver<Long> {

	public static int MAXTRIES = 5;
	
	private Twitter twitter;
	private TwitterProperties properties;
	
	public TwitterDriver(TwitterProperties properties) throws Exception {
		this.properties = properties;
		ConfigurationBuilder build = new ConfigurationBuilder();
        String oAuthAccessToken = this.properties.getoAuthAccessToken();
        String oAuthAccessTokenSecret = this.properties.getoAuthAccessTokenSecret();
        String oAuthConsumerKey = this.properties.getoAuthConsumerKey();
        String oAuthConsumerSecret = this.properties.getoAuthConsumerSecret();
        build.setOAuthAccessToken(oAuthAccessToken);
        build.setOAuthAccessTokenSecret(oAuthAccessTokenSecret);
        build.setOAuthConsumerKey(oAuthConsumerKey);
        build.setOAuthConsumerSecret(oAuthConsumerSecret);
        OAuthAuthorization auth = new OAuthAuthorization(build.build());
        this.twitter = new TwitterFactory().getInstance(auth);
        this.twitter.verifyCredentials();
	}
	
	public User getUser(Long id) throws TwitterException {
		User user = twitter.showUser(id);
		int c = 0;
		while (user == null && c++ < MAXTRIES) {
			try {
				Thread.sleep(properties.getMillis() * 100);
				user = twitter.showUser(id);
			} catch (Exception e) {
				//
			}
		}
		return user;
	}
	
	public User getUser(String screenName) throws TwitterException {
		User user = twitter.showUser(screenName);
		int c = 0;
		while (user == null && c++ < MAXTRIES) {
			try {
				Thread.sleep(properties.getMillis() * 100);
				user = twitter.showUser(screenName);
			} catch (Exception e) {
				//
			}
		}
		return user;
	}
	
	public Collection<Long> getFriends(String screenName) throws Exception {
		long cursor = -1;
		Collection<Long> ret = new LinkedList<Long>();
		do {
			IDs ids = getFriends(screenName, cursor);
			if (ids != null) {
				for (Long id : ids.getIDs()) {
					ret.add(id);
				}
				cursor = ids.getNextCursor();
			} else {
				cursor = 0;
			}
		} while (cursor != 0);
		return ret;
	}
	
	public Collection<Long> getFollowers(String screenName) throws Exception {
		long cursor = -1;
		Collection<Long> ret = new LinkedList<Long>();
		do {
			IDs ids = getFollowers(screenName, cursor);
			if (ids != null) {
				for (Long id : ids.getIDs()) {
					ret.add(id);
				}
				cursor = ids.getNextCursor();
			} else {
				cursor = 0;
			}
		} while (cursor != 0);
		return ret;
	}
	
	private IDs _getFriends(String screenName, long cursor) {
		IDs ret = null;
		try {
			ret = twitter.getFriendsIDs(screenName, cursor);
		} catch (Exception e) {
			//
		}
		return ret;
	}
	
	private IDs _getFollowers(String screenName, long cursor) {
		IDs ret = null;
		try {
			ret = twitter.getFollowersIDs(screenName, cursor);
		} catch (Exception e) {
			//
		}
		return ret;
	}
	
	private IDs getFriends(String screenName, long cursor) throws InterruptedException {
		IDs ret = _getFriends(screenName, cursor);
		int i = 0;
		while (ret == null && i++ < MAXTRIES) {
			Thread.sleep(properties.getMillis() * 100);
			ret = _getFriends(screenName, cursor);
		}
		return ret;
	}

	private IDs getFollowers(String screenName, long cursor) throws InterruptedException {
		IDs ret = _getFollowers(screenName, cursor);
		int i = 0;
		while (ret == null && i++ < MAXTRIES) {
			Thread.sleep(properties.getMillis() * 100);
			ret = _getFollowers(screenName, cursor);
		}
		return ret;
	}

	@Override
	public boolean hasUserData(Long screen) {
		boolean ret = false;
		Profile profile = TwitterDB.getProfile(screen);
		if (profile != null) {
			ret = true;
		}
		return ret;
	}

	@Override
	public void addUserData(Long id) throws Exception {
		//Screen screen = 
		TwitterDB.toScreen(id);
		User user = getUser(id);
		TwitterDB.addProfile(id, user);
		Collection<Long> friends = getFriends(user.getScreenName());
		TwitterDB.addFriends(id, friends);
		Collection<Long> followers = getFollowers(user.getScreenName());
		TwitterDB.addFollowers(id, followers);
		Collection<Long> contacts = new LinkedList<Long>(friends);
		contacts.addAll(followers);
	}
	
	@Override
	public Collection<Long> getConnections(Long id) {
		Collection<Screen> _friends = TwitterDB.getFriendsOf(id);
		Collection<Long> friends = new TreeSet<Long>();
		for (Screen _friend : _friends) {
			friends.add(_friend.getId());
		}
		return friends;
	}

	@Override
	public Person toPerson(Long id) throws Exception {
		if (!hasUserData(id)) {
			//addUserData(id);
			return null;
		}
		Person person = null;
		Profile profile = TwitterDB.getProfile(id);
		if (profile != null ) {
			person = new Person();
			person.setUri("http://semantictwitter.example.org/" + profile.getScreen());

			final String name = profile.getName();
			if (name != null) {
				person.addName(name);
			}
			
			Collection<Screen> friends = TwitterDB.getFriendsOf(id);
			if (friends != null) {
				for (Screen friend : friends) {
					Person pfriend = new Person();
					pfriend.setUri("http://semantictwitter.example.org/" + friend.getId());
					person.addKnows(pfriend);
				}
			}
			
			final String location = profile.getLocation();
			final String language = (profile.getLanguage() != null ? profile.getLanguage() : "en");
			
			if (location != null) {
				GeoLocation latlng = GeoUtils.findAddLocation(ServiceType.GOOGLE, location, language);
				if (latlng != null) {
					person.setLatitude(BigDecimal.valueOf(latlng.getLatitude()));
					person.setLongitude(BigDecimal.valueOf(latlng.getLongitude()));
				}
			}
		}
		return person;
	}

}
